﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;

namespace lib
{
    public class SecureConnectionClient
    {
        private SslStream sslStream = null;
        private X509Certificate serverCertificate = null;
        private BinaryFormatter formatter = new BinaryFormatter();

        public SecureConnectionClient(TcpClient tcp)
        {
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
            init(tcp);
        }

        public void ClosesslStream()
        {   
            sslStream.Close();
            sslStream.Dispose();
        }

        public void setServerCertificate(X509Certificate cert)
        {
            serverCertificate = cert;
        }

        public void init(TcpClient tcp)
        {
            sslStream = new SslStream(tcp.GetStream());
            Authenticate(tcp);
        }

        private void Authenticate(TcpClient tcp)
        {
            sslStream = new SslStream(tcp.GetStream(), false, new RemoteCertificateValidationCallback(CertificateValidationCallback));
            sslStream.AuthenticateAsClient("repelsteeltje");
            //showSslInfo("repelsteeltje", sslStream, true);
        }

        bool CertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
        {
            serverCertificate = certificate;
            const SslPolicyErrors ignoredErrors =
            SslPolicyErrors.RemoteCertificateChainErrors |
            SslPolicyErrors.RemoteCertificateNameMismatch;

            if ((sslPolicyErrors & ~ignoredErrors) != SslPolicyErrors.None)
            {
                Console.WriteLine("SSL Certificate Validation Error!");
                Console.WriteLine(sslPolicyErrors.ToString());
                return false;
            }
            else
                return true;
        }

        public void SendMessage(string message)
        {
            Byte[] sendBytes = Encoding.UTF8.GetBytes(message);
            sslStream.Write(sendBytes, 0, sendBytes.Length);
        }

        public string ReadMessage()
        {
            byte[] bytes = new byte[2048];

            // Read can return anything from 0 to numBytesToRead.  
            // This method blocks until at least one byte is read.
            sslStream.Read(bytes, 0, 2048);

            // Returns the data received from the host to the console. 
            string s = Encoding.UTF8.GetString(bytes);
            return s.Remove(s.IndexOf("\0"));
        }

        public void SendObject(Object o)
        {
            formatter.Serialize(sslStream, o);
        }

        
        public String ReadObjectAsString()
        {
            try
            {
                string naam = formatter.Deserialize(sslStream) as String;
                System.Diagnostics.Debug.WriteLine(naam);
                return naam;
            }
            catch
            {
                
                return "";
            }
        }

        public String ReadObjectAsString(object o)
        {
            try
            {
                string naam = o as String;
                System.Diagnostics.Debug.WriteLine(naam);
                return naam;
            }
            catch
            {

                return "";
            }
        }

        public Object ReadObject()
        {
                return formatter.Deserialize(sslStream);
        }

        public static void showSslInfo(string serverName, SslStream sslStream, bool verbose)
        {
            showCertificateInfo(sslStream.RemoteCertificate, verbose);

            Console.WriteLine("\n\nSSL Connect Report for : {0}\n", serverName);
            Console.WriteLine("Is Authenticated:            {0}", sslStream.IsAuthenticated);
            Console.WriteLine("Is Encrypted:                {0}", sslStream.IsEncrypted);
            Console.WriteLine("Is Signed:                   {0}", sslStream.IsSigned);
            Console.WriteLine("Is Mutually Authenticated:   {0}\n", sslStream.IsMutuallyAuthenticated);

            Console.WriteLine("Hash Algorithm:              {0}", sslStream.HashAlgorithm);
            Console.WriteLine("Hash Strength:               {0}", sslStream.HashStrength);
            Console.WriteLine("Cipher Algorithm:            {0}", sslStream.CipherAlgorithm);
            Console.WriteLine("Cipher Strength:             {0}\n", sslStream.CipherStrength);

            Console.WriteLine("Key Exchange Algorithm:      {0}", sslStream.KeyExchangeAlgorithm);
            Console.WriteLine("Key Exchange Strength:       {0}\n", sslStream.KeyExchangeStrength);
            Console.WriteLine("SSL Protocol:                {0}", sslStream.SslProtocol);


        }

        public static void showCertificateInfo(X509Certificate remoteCertificate, bool verbose)
        {
            Console.WriteLine("Certficate Information for:\n{0}\n", remoteCertificate.GetName());
            Console.WriteLine("Valid From:      \n{0}", remoteCertificate.GetEffectiveDateString());
            Console.WriteLine("Valid To:        \n{0}", remoteCertificate.GetExpirationDateString());
            Console.WriteLine("Certificate Format:     \n{0}\n", remoteCertificate.GetFormat());

            Console.WriteLine("Issuer Name:     \n{0}", remoteCertificate.GetIssuerName());

            if (verbose)
            {
                Console.WriteLine("Serial Number:   \n{0}", remoteCertificate.GetSerialNumberString());
                Console.WriteLine("Hash:            \n{0}", remoteCertificate.GetCertHashString());
                Console.WriteLine("Key Algorithm:   \n{0}", remoteCertificate.GetKeyAlgorithm());
                Console.WriteLine("Key Algorithm Parameters:     \n{0}", remoteCertificate.GetKeyAlgorithmParametersString());
                Console.WriteLine("Public Key:     \n{0}", remoteCertificate.GetPublicKeyString());
            }

        }
    }
}
